<!-- 表单 》 富文本 -->

<template>
  <el-form-item ref="item" v-bind="formItem" :style="itemStyle">
    <!-- 隐藏输入框，触发校验 -->
    <el-input v-show="false" :value="model" />

    <!-- 标签 -->
    <template v-if="hasLabelSlot" #label>
      <slot name="label" :label="formItem.label">{{ formItem.label }}</slot>
    </template>

    <div v-show="false" ref="text" v-html="model" />

    <!-- 富文本 -->
    <div :class="richClass" :style="editorStyle">
      <!-- 菜单 -->
      <div ref="menu" :class="`${prefix}-menu`" />

      <!-- 编辑器 -->
      <EasyScroll :auto="auto" :wide="wide">
        <div ref="editor" :class="`${prefix}-editor`" />
      </EasyScroll>

      <!-- 全屏切换 -->
      <div v-if="!noFull" class="w-e-toolbar" :class="`${prefix}-btn`">
        <div class="w-e-menu" @click="full = !full">
          <i v-if="full" class="w-e-icon-fullscreen_exit" />
          <i v-else class="w-e-icon-fullscreen" />
        </div>
      </div>
    </div>

    <!-- 计数 -->
    <div v-if="!noCount" :class="`${prefix}-count`">{{ count }}/{{ max }}</div>
  </el-form-item>
</template>

<script>
// ============================== 导入依赖 ============================== //

import CORE from '@/CORE'
import CONST from './CONST'
import mixForm from '../mixForm'
import FormFile from '../FormFile'

const name = 'FormRichText'

// ============================== 导出组件 ============================== //

export default {
  /**
   * 名称定义 (实际使用名称)
   */
  name,

  /**
   * 混入列表 (配置选项合并)
   */
  mixins: [mixForm],

  /**
   * 属性注册 (抽取以便查阅)
   */
  props: CORE.WEB[name],

  /**
   * 状态数据 (函数返回对象)
   */
  data() {
    return {
      model: '', // 内部绑定值
      EMPTY: '', // 空值常量

      editor: null, // 编辑器
      count: 0, // 字数
      full: false, // 是否全屏
    }
  },

  /**
   * 计算属性 (慎用箭头函数)
   */
  computed: {
    /**
     * 业务标识
     */
    businessTag: FormFile.computed.businessTag,

    /**
     * 样式类前缀
     */
    prefix: () => `CoreWeb-${name}`,

    /**
     * 表单项样式
     */
    itemStyle() {
      const { height, getSize } = this
      return height ? { height: getSize(height) } : null
    },

    /**
     * 编辑器样式
     */
    editorStyle() {
      const { height, getSize, full } = this
      return height && !full ? { height: getSize(height) } : null
    },

    /**
     * 富文本样式类
     */
    richClass() {
      const { prefix } = this
      const fullStr = this.full ? ` ${prefix}-full` : ''
      const showStr = this.isDisabled ? ` ${prefix}-show` : ''
      const emptyStr = this.count ? '' : ` ${prefix}-empty`
      return `${prefix}${fullStr}${showStr}${emptyStr}`
    },

    /**
     * 校验方法
     */
    validator() {
      const { check, max, noCount, count } = this
      if (noCount && !check) return null
      return (rule, value, callback) => {
        const errMsg = check ? check(value) : ''
        if (value && !noCount && count > max) callback(new Error(`不能超过 ${max} 个字`))
        else if (value && errMsg) callback(new Error(errMsg))
        else callback()
      }
    },

    /**
     * 编辑器配置
     */
    config() {
      const { tip, tipPre, label, maxImage, maxVideo, noImage, noVideo, noLink, getUpload } = this
      const config = {
        ...CORE.clone(CONST.CONFIG),
        customAlert: (msg, type) => CORE.MSG[type](msg),
        // height,
        placeholder: tip || `${tipPre}${label}`,
        uploadImgMaxSize: maxImage * 1048576,
        uploadVideoMaxSize: maxVideo * 1048576,
        onchange: this.onChange,
      }

      if (!noImage) config.customUploadImg = getUpload('image')
      if (!noVideo) config.customUploadVideo = getUpload('video')

      if (!noLink) config.menus.push('link')
      if (!noImage) config.menus.push('image')
      if (!noVideo) config.menus.push('video')

      return config
    },
  },

  /**
   * 监听属性 (深度 deep 首次 immediate)
   */
  watch: {
    /**
     * 监听对象
     */
    obj: {
      immediate: true,
      handler() {
        this.editor && this.setContent()
      },
    },

    /**
     * 监听禁用
     */
    isDisabled() {
      this.editor && this.setDisable()
    },
  },

  /**
   * 挂载完成 (先抽取再调用)
   */
  mounted() {
    this.createEditor().catch((e) => CORE.logError('富文本初始化', {}, e))
  },

  /**
   * 本地方法 (轻量重要在前)
   */
  methods: {
    /**
     * 是否不等 (返回相等，不走 mixForm 的逻辑)
     */
    isDiff() {
      return false
    },

    /**
     * 编辑器创建
     */
    async createEditor() {
      // 等待脚本
      await CORE.loadJS(this.cdnSrc, this.cdnName)

      // 绑定元素
      const Editor = window.wangEditor
      this.editor = new Editor(this.$refs.menu, this.$refs.editor)

      // 合并配置
      CORE.merge(this.editor.config, this.config)

      // 渲染创建
      this.editor.create()

      // 设置内容
      this.setContent()
    },

    /**
     * 设置内容
     */
    async setContent() {
      const html = this.obj[this.prop]
      const matched = CONST.REGEXP.exec(html)
      this.model = matched ? matched[1] : html || ''
      this.editor.txt.html(this.model)
      this.setDisable()
    },

    /**
     * 设置禁用
     */
    setDisable() {
      this.editor[this.isDisabled ? 'disable' : 'enable']()
    },

    /**
     * 改变事件
     */
    async onChange(html) {
      this.model = html
      const { editor, $set } = this

      if (/^<\w+><br\/><\/\w+>$/.test(html)) {
        editor.txt.html('') // 使之进入下一次 onChange
        return
      }

      const value = html ? `${CONST.PREFIX}${html}${CONST.SUFFIX}` : ''
      $set(this.obj, this.prop, value)
      this.$emit('change', value)

      // 记录字数
      if (value) {
        await this.$nextTick()
        const container = ((this.$refs.editor || {}).children || [])[0] || {}
        const content = (container.children || [])[0] || {}
        this.count = (content.innerText || '').replace(/\n/g, '').length
      } else {
        this.count = 0
      }
    },

    /**
     * 获取文件夹
     */
    getFolder: FormFile.methods.getFolder,

    /**
     * 获取上传方法
     */
    getUpload(type) {
      return async (files, insert) => {
        // 全局加载中
        CORE.loading()

        // 批量上传
        for (const file of files) {
          try {
            const folder = this.getFolder()
            const tag = this.businessTag
            const path = await CORE[`${type}Upload`](file, folder, tag)
            insert(`${CORE.FILE.PREFIX}/${path}${CORE.FILE.SUFFIX}`)
          } catch (e) {
            //
          }
        }

        // 关闭加载
        CORE.unloading()
      }
    },
  },
}
</script>
